﻿using System.Text;
using System.Windows;
using System.Windows.Controls;
using HOPETechnik;
using System.Collections.ObjectModel;
using HOPETechnik.XMLParser;
using Microsoft.Win32;
using System.IO;

namespace TestSocket
{    
    public partial class ConnectedSocketsPanel : UserControl, IPanel, ISocketEventsCallback
    {
        class SocketInfo
        {
            string m_strLocalAddr;
            string m_strRemoteAddr;
            public bool bBinary;
            CAsyncSocket me;

            public SocketInfo(CAsyncSocket sock)
            {
                me = sock;
                m_strLocalAddr = sock.LocalInfo;
                m_strRemoteAddr = sock.PeerInfo;
            }

            public CAsyncSocket myself { get { return me; } }
            public string SrcAddress { get { return m_strLocalAddr; } }
            public string DestAddress { get { return m_strRemoteAddr; } }
            public Visibility CanSendFile
            {
                get
                {
                    if (bBinary)
                        return Visibility.Visible;
                    else
                        return Visibility.Hidden;
                }
            }
            public Visibility CanSendMsg
            {
                get
                {
                    if (bBinary)
                        return Visibility.Hidden;
                    else
                        return Visibility.Visible;
                }
            }
        }

        MainWindow m_wnd;
        CAsyncSocket m_MsgSock;
        CAsyncSocket m_binSock;
        ObservableCollection<SocketInfo> m_Sockets;        
        StringBuilder m_sb;
        CRequestFactory m_requests;
        CXMLParser m_parser;
        string m_strFile2Send = string.Empty;
        bool m_bSendingFile = false;
        BinaryReader m_fileReader;        

        void UpdateSocketList(CAsyncSocket sock, bool bBinary, bool bAdd)
        {
            if (bAdd)
            {
                SocketInfo info = new SocketInfo(sock);                
                info.bBinary = bBinary;
                m_Sockets.Add(info);
            }
            else
            {
                foreach (SocketInfo info in m_Sockets)
                {
                    if (info.myself == sock)
                    {
                        m_Sockets.Remove(info);
                        break;
                    }
                }
            }
        }


        public ConnectedSocketsPanel(MainWindow wnd)
        {
            InitializeComponent();
            m_wnd = wnd;
            m_MsgSock = new CAsyncSocket(this);
            m_binSock = new CAsyncSocket(this);
            m_Sockets = new ObservableCollection<SocketInfo>();
            m_sb = new StringBuilder();
            SocketList.ItemsSource = m_Sockets;
            m_requests=new CRequestFactory();
            m_parser = new CXMLParser();
        }

        void SendMessage(string strMsg)
        {
            string s = strMsg + CAsyncSocket.strEndingDelimiter;
            byte[] data = Encoding.UTF8.GetBytes(s);                
            int nLength = data.Length;
            if (m_MsgSock.sendBuffer.nBufferSize < nLength)
            {
                m_wnd.dprintf("Error, msg too long!");
                return;
            }
            System.Buffer.BlockCopy(data, 0, m_MsgSock.sendBuffer.buffer, 0, nLength);
            m_MsgSock.sendBuffer.nPayloadSize = nLength;            
            m_MsgSock.SendData();            
        }

        public void OnActivate()
        {            
            m_MsgSock.Connect(m_wnd.strDestIP, int.Parse(m_wnd.strMsgPort), m_wnd.strSrcIP);
            m_binSock.Connect(m_wnd.strDestIP, int.Parse(m_wnd.strBinaryPort), m_wnd.strSrcIP);
        }

        public void OnDeactivate(){}        

        public void OnAppClosing()
        {
            if (m_MsgSock!=null)
            {
                m_MsgSock.Disconnect();
                m_MsgSock.Dispose();
            }
            if (m_binSock != null)
            {
                m_binSock.Disconnect();
                m_binSock.Dispose();
            }
        }

        public void OnSocketAccepted(CAsyncSocket acceptedSocket){}
        
        public void OnSocketConnected(CAsyncSocket sock)
        {
            string Msg = string.Format(":" + m_wnd.strMsgPort);
            string bin = string.Format(":" + m_wnd.strBinaryPort);
            bool binary = false;

            if (sock.PeerInfo.IndexOf(Msg) > -1)
            {
                StringBuilder build = new StringBuilder();
                m_wnd.dprintf("connected msg socket to " + sock.PeerInfo);
            }
            else if (sock.PeerInfo.IndexOf(bin) > -1)
            {
                m_wnd.dprintf("connected binary socket to " + sock.PeerInfo);
                binary = true;
            }            
            Dispatcher.Invoke(new System.Action(() => UpdateSocketList(sock, binary, true)));
        }

        public void OnSocketDisconnected(CAsyncSocket sock)
        {
            m_wnd.dprintf(sock.PeerInfo+ " disconnected");
            Dispatcher.Invoke(new System.Action(() => UpdateSocketList(sock, false, false)));
            sock.Dispose();
            if (sock == m_MsgSock)
                m_MsgSock = null;
            else if (sock == m_binSock)
                m_binSock = null;
        }

        public void OnSocketDataReceived(CAsyncSocket sock)
        {
            string str = Encoding.UTF8.GetString(sock.rcvBuffer.buffer, 0, sock.rcvBuffer.nPayloadSize);
            m_sb.Append(str);
            if (str.IndexOf(CAsyncSocket.strEndingDelimiter) > -1)
            {
                str = m_sb.ToString();
                string s = str.Substring(0, str.IndexOf(CAsyncSocket.strEndingDelimiter));
                CBaseRequest req=m_requests.getRequest(s);
                if (req != null)
                {
                    m_parser.setXMLStream(req);
                    m_parser.Parse();
                    if (req.GetType() == typeof(CTextMessage))
                        m_wnd.dprintf(sock.PeerInfo + ">> " + ((CTextMessage)req).strMessage);
                    else if (req.GetType() == typeof(CFileSendResp))
                    {
                        if (((CFileSendResp)req).bReady2Recv)                            
                            StartSendingFile();
                        else
                        {
                            m_wnd.dprintf("remote rejected file send request");
                            m_strFile2Send = string.Empty;
                        }
                    }
                }                        
                m_sb.Clear();
            }
        }

        void StartSendingFile()
        {            
            //m_wnd.dprintf("sending over file content now");
            //m_binSock.SendFile(m_strFile2Send);
            m_fileReader = new BinaryReader(File.Open(m_strFile2Send, FileMode.Open));
            SendFileContent();
        }

        void SendFileContent()
        {
            int nRead = m_fileReader.Read(m_binSock.sendBuffer.buffer, 0, m_binSock.sendBuffer.nBufferSize);
            if (nRead>0)
            {                
                m_binSock.sendBuffer.nPayloadSize = nRead;
                m_binSock.SendData();
                m_bSendingFile = true;
                Dispatcher.Invoke(new System.Action(() => UpdateFileSendProgress(nRead)));
            }
            else //EOF reached
            {
                m_fileReader.Close();
                m_fileReader.Dispose();
                m_fileReader = null;
                m_bSendingFile = false;                
                m_wnd.dprintf("file content sent over");                
            }
        }

        void UpdateFileSendProgress(long Sent)
        {
            SendFilePB.Value += Sent;
        }
        /*public void OnSocketFileSent(CAsyncSocket sock)
        {
            CBinaryContentSendCompleted req = (CBinaryContentSendCompleted)m_requests.getRequest("<BinaryContentSendCompleted>");
            req.strSocketAddr = sock.LocalInfo;
            SendMessage(req.toXML());
            m_wnd.dprintf("file content sent over"); 
        }*/
        public void OnSocketDataSent(CAsyncSocket sock)
        {            
            if (m_bSendingFile)
                SendFileContent();
            else
                m_wnd.dprintf(sock.LocalInfo + " Ready to send");
        }

        public void ReportError(CAsyncSocket sock, System.Net.Sockets.SocketException ex)
        {
            m_wnd.dprintf("Error>>" + ex.ToString());
            if (ex.ErrorCode == 87)
                OnSocketDisconnected(sock);            
        }

        private void OnSendMsg(object sender, RoutedEventArgs e)
        {            
            CTextMessage req = (CTextMessage)m_requests.getRequest("<TextMessage>");
            if (req != null)
            {
                req.strMessage = tbMsg.Text;
                string s = req.toXML();
                SendMessage(s);
            }
        }

        private void OnSendFile(object sender, RoutedEventArgs e)
        {
            Button btn = (Button)sender;
            CAsyncSocket sock = (CAsyncSocket)btn.Tag;
            OpenFileDialog dlg = new OpenFileDialog();
            if (dlg.ShowDialog()==true)
            {
                CFileSendRequest req=(CFileSendRequest) m_requests.getRequest("<FileSendReq>");
                req.strFilePath = dlg.FileName;
                req.strSocketAddr=sock.LocalInfo;
                SendMessage(req.toXML());
                m_wnd.dprintf("initiating file send req");
                m_strFile2Send = dlg.FileName;
                SendFilePB.Maximum = req.m_lFileSize;
                SendFilePB.Value = 0.0;
            }
   
        }

        private void OnDisconnect(object sender, RoutedEventArgs e)
        {
            Button btn = (Button)sender;
            CAsyncSocket sock = (CAsyncSocket)btn.Tag;
            sock.Disconnect();
            UpdateSocketList(sock, false, false);
            sock.Dispose();
        }
    }
}
